﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Windows.Threading;

namespace PTiRouZ.Net.SilverLight.Api
{
    public class ApiOpenReadCompletedEventArgs : EventArgs
    {
        public String Result;
        public String Url;

        public bool Cancelled;
        public Exception Error;
        public object UserState;
    }

    public class ApiErrorEventArgs : EventArgs
    {
        public Exception Exception;
        public String Message;
    }

    /// <summary>Class permettant d'utiliser toutes les fonctionnalité de l'api.
    ///     <c>http://www.domain.com/api/</c>
    /// </summary>
    public class api
    {
        protected delegate void APICallCompletedEventHandler(object sender, ApiOpenReadCompletedEventArgs e);
        protected event APICallCompletedEventHandler APICallCompleted;

        public delegate void APIErrorEventHandler(object sender, ApiErrorEventArgs e);
        public event APIErrorEventHandler APICallError;

        private string api_user;
        private string api_pass;
        private string api_baseurl;
        private string usernamepassword;
        private NetworkCredential NetCredential;

        private WebClient proxy;

        public bool AddHeaderCredentials = false;

        public override string ToString()
        {
            return String.Format("User : {0} , Pass : {1}", api_user, api_pass); 
        }
        
        /// <summary>Constructeur de la classe</summary>
        /// <param name="_user">Login pour authentification sur l'api</param>
        /// <param name="_pass">Mot de passe pour authentification sur l'api</param>
        /// <param name="_baseurl">Base de l'url pour accéder à l'api</param>
        public api(string _baseurl)
        {
            api_baseurl = _baseurl;
        }

        /// <summary>Constructeur de la classe</summary>
        /// <param name="_user">Login pour authentification sur l'api</param>
        /// <param name="_pass">Mot de passe pour authentification sur l'api</param>
        /// <param name="_baseurl">Base de l'url pour accéder à l'api</param>
        public api(string _user, string _pass, string _baseurl)
        {
            api_user = _user;
            api_pass = _pass;
            api_baseurl = _baseurl;
            usernamepassword = api_user + ":" + api_pass;

            WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp);
            NetCredential = new NetworkCredential(_user, _pass);
        }

        /// <summary>Permet d'executer les requêtes en mode GET.</summary>
        /// <param name="_url">Url de la fonction de l'api à executer</param>
        /// <returns>Contenu du résultat de l'appel à l'api</returns>
        protected void GetUrlApi(string _url, List<KeyValuePair<string, string>> queryValue)
        {
            proxy = new WebClient();
            proxy.Credentials = NetCredential;
            proxy.UseDefaultCredentials = false; // must be set to false if providing your own credentials 

            if (AddHeaderCredentials)
            {
                var base64Creds = Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", api_user, api_pass)));
                proxy.Headers["Authorization"] = "Basic " + base64Creds;
            }

            proxy.DownloadStringCompleted += new DownloadStringCompletedEventHandler(proxy_DownloadStringCompleted);

            string query = queryValue.ToConcatenatedString(pair => pair.Key == null ? pair.Value : pair.Key + "=" + pair.Value, "&");

            if (query != String.Empty)
                _url += '?' + query;

            proxy.DownloadStringAsync(new Uri(api_baseurl + _url, UriKind.Absolute));

        }
        /// <summary>Permet d'executer les requêtes en mode GET.</summary>
        /// <param name="_url">Url de la fonction de l'api à executer</param>
        /// <returns>Contenu du résultat de l'appel à l'api</returns>
        protected void GetUrlApi(string _url)
        {
            GetUrlApi(_url, new List<KeyValuePair<string, string>>());
        }

        void proxy_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            ApiOpenReadCompletedEventArgs APIEventArgs = new ApiOpenReadCompletedEventArgs();
            try
            {
                APIEventArgs.Result = e.Result;
                APIEventArgs.UserState = e.UserState;
                APIEventArgs.Cancelled = e.Cancelled;
                APIEventArgs.Error = e.Error;
                APIEventArgs.Url = "";
            }
            catch (Exception ue)
            {
                APIEventArgs.Result = null;
                APIEventArgs.Error = ue;

                Exception ex = ue;
                while (ex.InnerException != null)
                    ex = ex.InnerException;

                //APICallError.Invoke(sender, new ApiErrorEventArgs() { Exception = ue, Message = ex.Message });
            }
            finally
            {
                proxy.DownloadStringCompleted -= new DownloadStringCompletedEventHandler(proxy_DownloadStringCompleted);
            }

            if (APIEventArgs.Result != null)
            {
                if (APICallCompleted != null)
                    APICallCompleted.Invoke(sender, APIEventArgs);
            }
        }

        /// <summary>Permet d'executer les requêtes en mode POST.</summary>
        /// <param name="_url">Url de la fonction de l'api à executer</param>
        /// <returns>Contenu du résultat de l'appel à l'api</returns>
        protected void PostUrlApi(string _url)
        {
            PostUrlApi(_url, new List<KeyValuePair<string, string>>());
        }

        protected void PostUrlApi(string _url, List<KeyValuePair<string, string>> queryValue)
        {
            WebClient proxy = new WebClient();
            proxy.Credentials = NetCredential;
            proxy.UseDefaultCredentials = false; // must be set to false if providing your own credentials             

            proxy.UploadStringCompleted += new UploadStringCompletedEventHandler(proxy_UploadStringCompleted);

            string query = queryValue.ToConcatenatedString(pair => pair.Key == null ? pair.Value : pair.Key + "=" + pair.Value, "&");

            if (query != String.Empty)
                _url += '?' + query;

            proxy.UploadStringAsync(new Uri(api_baseurl + _url), "POST");//, String.Empty);
        }

        /// <summary>Permet de se connecter à l'api avec authentifiaction et d'executer les requêtes en mode POST.</summary>
        /// <param name="_url">Url de la fonction de l'api à executer</param>
        /// <param name="querystring">Paramètre(s) à passer à la fonction de l'api</param>
        /// <param name="file">Nom du paramètre dans <paramref name="querystring"/> contenant le fichier à envoyé</param>
        /// <returns>Contenu du résultat de l'appel à l'api</returns>        
        protected StreamReader PostUrlApi(string _url, List<KeyValuePair<string, string>> queryValue, String FileKey, Stream fileStream)
        {
            bool FileToSend = fileStream.Length > 0;

            if (FileToSend)
            {
                string url = this.api_baseurl + _url;

                Uri uri = new Uri(url);

                string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
                StringBuilder sb = new StringBuilder();

                if (queryValue != null)
                {
                    foreach (KeyValuePair<string, string> keyvalue in queryValue)
                    {
                        sb.Append("--");
                        sb.Append(boundary);
                        sb.Append("\r\n");

                        if (FileToSend && keyvalue.Key.Equals(FileKey))
                        {
                            sb.Append("Content-Disposition: form-data; name=\"");
                            sb.Append(keyvalue.Key);
                            sb.Append("\"; filename=\"");
                            sb.Append(keyvalue.Value);
                            sb.Append("\"");
                            sb.Append("\r\n");
                            sb.Append("Content-Type: ");
                            sb.Append("application/octet-stream");
                            sb.Append("\r\n");
                        }
                        else
                        {
                            sb.Append("Content-Disposition: form-data; name=\"" + keyvalue.Key + "\"");
                            sb.Append("\r\n");
                            sb.Append("\r\n");
                            sb.Append(keyvalue.Value);
                        }

                        sb.Append("\r\n");
                    }
                }

                UTF8Encoding encoding = new UTF8Encoding();

                /*Dispatcher.BeginInvoke(() =>
                {
                    txtUploadResult.Text = "Buffering data...";
                });*/

                byte[] postContents = encoding.GetBytes(sb.ToString());

                byte[] fileContents = new byte[fileStream.Length];
                fileStream.Read(fileContents, 0, fileContents.Length);
                fileStream.Close();

                byte[] postFooter;
                long length;

                if (FileToSend)
                {
                    postFooter = encoding.GetBytes("\r\n--" + boundary + "--"); /*"\r\n"*/
                    length = postContents.Length + fileContents.Length + postFooter.Length;
                }
                else
                {
                    postFooter = encoding.GetBytes("--" + boundary + "");
                    length = postContents.Length + postFooter.Length;
                }

                byte[] dataBuffer = new byte[length];
                Buffer.BlockCopy(postContents, 0, dataBuffer, 0, postContents.Length);
                Buffer.BlockCopy(fileContents, 0, dataBuffer, postContents.Length, fileContents.Length);
                Buffer.BlockCopy(postFooter, 0, dataBuffer, postContents.Length + fileContents.Length, postFooter.Length);


                HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(uri);
                webrequest.ContentType = "multipart/form-data; boundary=" + boundary + "";
                webrequest.Method = "POST";

                //Identifier sur l'api                
                webrequest.Credentials = NetCredential;
                webrequest.UseDefaultCredentials = false; // must be set to false if providing your own credentials             
                webrequest.ContentLength = dataBuffer.Length;

                /*Dispatcher.BeginInvoke(() =>
                {
                    txtUploadResult.Text = "Waiting for request stream...";
                });*/

                webrequest.BeginGetRequestStream(new AsyncCallback(GotRequestStreamForUpload), new SendFileAsyncState(webrequest, dataBuffer));
                //{"status":"ok","photo_id":5902420,"album_id":"12327"}
            }

            return null;
        }

        private void GotRequestStreamForUpload(IAsyncResult result)
        {
            /*Dispatcher.BeginInvoke(() =>
            {
               txtUploadResult.Text = "Uploading photo...";
            });*/

            HttpWebRequest request = ((SendFileAsyncState)result.AsyncState).Request;
            byte[] data = ((SendFileAsyncState)result.AsyncState).Data;

            Stream writeStream = request.EndGetRequestStream(result);

            foreach (byte b in data)
            {
                writeStream.WriteByte(b);
            }

            writeStream.Close();

            /*Dispatcher.BeginInvoke(() =>
            {
                txtUploadResult.Text = "Waiting for response stream...";
            });*/

            request.BeginGetResponse(new AsyncCallback(GotResponseStreamForUpload), request);
        }

        private void GotResponseStreamForUpload(IAsyncResult result)
        {
            /*Dispatcher.BeginInvoke(() =>
            {
                spProgress.Visibility = System.Windows.Visibility.Collapsed;
            });*/

            HttpWebRequest request = (HttpWebRequest)result.AsyncState;
            HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);

            using (StreamReader streamReader1 = new StreamReader(response.GetResponseStream()))
            {
            ApiOpenReadCompletedEventArgs APIEventArgs = new ApiOpenReadCompletedEventArgs();
            try
            {
                APIEventArgs.Result = streamReader1.ReadToEnd();
                /*APIEventArgs.UserState = e.UserState;
                APIEventArgs.Cancelled = e.Cancelled;
                APIEventArgs.Error = e.Error;
                APIEventArgs.Url = "";*/
                APICallCompleted.Invoke(this, APIEventArgs);
            }
            catch (Exception ue)
            {
                APIEventArgs.Result = null;
                APIEventArgs.Error = ue;

                Exception ex = ue;
                while (ex.InnerException != null)
                    ex = ex.InnerException;
            }

            if (APIEventArgs.Result != null)
                APICallCompleted.Invoke(this, APIEventArgs);

            }
        }


        public class SendFileAsyncState
        {
            private HttpWebRequest _Request;
            public HttpWebRequest Request
            {
                get { return _Request; }
                set { _Request = value; }
            }

            private byte[] _Data;
            public byte[] Data
            {
                get { return _Data; }
                set { _Data = value; }
            }

            /// <summary>
            /// Initializes a new instance of the SendPhotoAsyncState class.
            /// </summary>
            /// <param name="request"></param>
            /// <param name="data"></param>
            public SendFileAsyncState(HttpWebRequest request, byte[] data)
            {
                _Request = request;
                _Data = data;
            }

        }

        void proxy_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
        {
            ApiOpenReadCompletedEventArgs APIEventArgs = new ApiOpenReadCompletedEventArgs();
            try
            {
                APIEventArgs.Result = e.Result;
                APIEventArgs.UserState = e.UserState;
                APIEventArgs.Cancelled = e.Cancelled;
                APIEventArgs.Error = e.Error;
                APIEventArgs.Url = "";
                APICallCompleted.Invoke(sender, APIEventArgs);
            }
            catch (Exception ue)
            {
                APIEventArgs.Result = null;
                APIEventArgs.Error = ue;

                Exception ex = ue;
                while (ex.InnerException != null)
                    ex = ex.InnerException;
            }

            if (APIEventArgs.Result != null)
                APICallCompleted.Invoke(sender, APIEventArgs);
        }


    }
}
